/*
 *  Filename:lsv_volume_storage_file_impl.c
 *  Description:
 *
 *  Created on: 2017年3月2日
 *  Author: Asdf(825674301)
 */
#include "config.h"

#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#define DBG_SUBSYS S_LIBSTORAGE

#include "yid.h"
#include "dbg.h"
#include "volume_proto.h"

#include "lsv_volume_storage.h"

int lsv_volume_storage_lich_create(lsv_volume_proto_t *lsv_info) {
        (void)lsv_info;
        return 0;
}

int lsv_volume_storage_lich_delete(lsv_volume_proto_t *lsv_info) {
        (void)lsv_info;
        return 0;
}

int lsv_volume_storage_lich_open(lsv_volume_proto_t *lsv_info) {
        (void)lsv_info;
        return 0;
}

int lsv_volume_storage_lich_close(lsv_volume_proto_t *lsv_info) {
        (void)lsv_info;
        return 0;
}

/**
 *
 * @param lsv_info
 * @param _buf
 * @param type
 * @param offset
 * @param size
 * @return == 0 success
 * @return > 0 error code
 */
int lsv_volume_storage_lich_write(lsv_volume_proto_t *lsv_info, int8_t *_buf, lsv_u8_t type,
                                  lsv_u64_t offset, lsv_u32_t size) {

        int ret;
        volume_proto_t *volume_proto = NULL;
        io_opt_t io_opt;
        io_t io;
        buffer_t buf;

#if ENABLE_PROFILE
        struct timeval t1, t2;
        _gettimeofday(&t1, NULL);
#endif

        volume_proto = lsv_info->volume_proto;

        // @malloc ERR1
        mbuffer_init(&buf, 0);
#if 1
        // TODO 托管的内存，由谁来释放？
        mbuffer_attach(&buf, _buf, size, NULL);
#else
        ret = mbuffer_copy(&buf, (const char *)_buf, size);
        if (unlikely(ret)) {
                ret = ENOMEM;
                GOTO(ERR1, ret);
        }
#endif

        // 混合存储的情况下，log直接落到hdd上
        // 0: ssd, 1: hdd, -1: auto tier，不启用
        int priority = (type == LSV_STORAGE_TYPE_SSD) ? 0: 1;
        io_opt_init(&io_opt, 0, priority, 0, IO_OPT_SET_PRIORITY);

        LSV_DBUG("chunk_id %ju type %d priority %d\n", offset/1048576, type, priority);

        // 需要写入SSD的数据，不经过SSD cache
        io_init(&io, &volume_proto->chkid, NULL, offset, size, (type == LSV_STORAGE_TYPE_SSD) ? __FILE_ATTR_DIRECT__ : 0);

        
        offset =  (offset + size + 1073741824) / 1073741824 * 1073741824;
        offset -= size;
        ret = volume_proto_truncate(volume_proto, offset, size);
        if (unlikely(ret)) {
                GOTO(ERR1, ret);
        }

        ret = volume_proto_write_raw(volume_proto, &io_opt, &io, &buf);
        if (unlikely(ret)) {
                GOTO(ERR1, ret);
        }

        mbuffer_free(&buf);

#if ENABLE_PROFILE
        _gettimeofday(&t2, NULL);
        int64_t used = _time_used(&t1, &t2);
        LSV_DBUG("vol %ju off %ju size %u type %u used %ju\n",
               lsv_info->ino,
               offset,
               size,
               type,
               used);

        op_type_t op;
        if (size == 4096) {
                op = WRITE_4K;
        } else if (size == 8192) {
                op = WRITE_8K;
        } else if (size == 1048576) {
                op = WRITE_1M;
        } else {
                op = OP_MAX;
        }
        if (op != OP_MAX) {
                lsv_info->op_stat[op].count += 1;
                lsv_info->op_stat[op].used += used;
        }
#endif

        return 0;
ERR1:
        mbuffer_free(&buf);

        DFATAL("%s ret %d off %ju size %u\n", strerror(ret), ret, offset, size);
        ret = _errno(ret);
        //if (ret > 0) {
        //        ret = -ret;
        //}
        return ret;
}

/**
 * @todo 如果读取部分数据后，本过程失败，且_buf是有效的用户缓冲区，部分更新，这样会造成数据损坏。
 *
 * @param lsv_info
 * @param offset
 * @param size
 * @param _buf
 * @return
 */
int lsv_volume_storage_lich_read(lsv_volume_proto_t *lsv_info, uint64_t offset, uint32_t size, int8_t *_buf) {

        int ret;
        volume_proto_t *volume_proto = NULL;
        io_t io;
        buffer_t buf;

#if ENABLE_PROFILE
        struct timeval t1, t2;
        _gettimeofday(&t1, NULL);
#endif

        volume_proto = lsv_info->volume_proto;

        // @malloc ERR1
        mbuffer_init(&buf, 0);

        io_init(&io, &volume_proto->chkid, NULL, offset, size, 0);

        ret = volume_proto_read_raw(volume_proto, &io, &buf);
        if (unlikely(ret)) {
                GOTO(ERR1, ret);
        }

        size = buf.len;
        mbuffer_get(&buf, _buf, size);

        mbuffer_free(&buf);

#if ENABLE_PROFILE
        _gettimeofday(&t2, NULL);
        DINFO("vol %ju off %ju size %u time %ju\n",
               lsv_info->ino,
               offset,
               size,
               _time_used(&t1, &t2));
#endif

        return 0;
ERR1:
        mbuffer_free(&buf);

        DFATAL("%s ret %d off %ju size %u\n", strerror(ret), ret, offset, size);
        ret = _errno(ret);
        //if (ret > 0) {
        //        ret = -ret;
        //}
        return ret;
}

lsv_volume_storage_operations lich_op = {
        .create = lsv_volume_storage_lich_create,
        .delete = lsv_volume_storage_lich_delete,
        .open = lsv_volume_storage_lich_open,
        .close = lsv_volume_storage_lich_close,
        .write = lsv_volume_storage_lich_write,
        .read = lsv_volume_storage_lich_read,
};
